﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Windows.Markup;
using System.Collections.ObjectModel;
using GestIT;
namespace BodyGestIT
{
    [ContentProperty("Children")]
    public abstract class GestureComposite : GestureComponent
    {

        public ObservableCollection<GestureComponent> Children { get; private set; }

        public GestureComposite()
            : base()
        {
            Children = new ObservableCollection<GestureComponent>();
            this.Visibility = Visibility.Collapsed;
        }

        internal abstract override GestIT.TmpExp InstantiateExp();

        protected GestIT.TmpExp CreateCompositeExpression(GestIT.TmpOperator op)
        {
            switch (op)
            {
                case GestIT.TmpOperator.Enabling: goto case GestIT.TmpOperator.Choice;
                case GestIT.TmpOperator.Parallel: goto case GestIT.TmpOperator.Choice;
                case GestIT.TmpOperator.Disabling: goto case GestIT.TmpOperator.Choice;
                case GestIT.TmpOperator.SynchronizedParallel: goto case GestIT.TmpOperator.Choice;
                case GestIT.TmpOperator.Choice:

                    // create a list of binary expressions, since
                    // the operator are associative
                    return ApplyAssociative(Children.Count - 1,  op);

                case GestIT.TmpOperator.OrderIndependence:
                    return ApplyNotAssociative(op);

                default: return null;
            }
        }

        private GestIT.ComplexTmpExp ApplyNotAssociative(GestIT.TmpOperator op)
        {
            ComplexTmpExp toRet = new ComplexTmpExp();
            toRet.Operator = op;

            foreach (GestureComponent component in Children)
            {
                TmpExp child = component.InstantiateExp();
                toRet.AddOperand(child);
            }

            AttachEvents(toRet);

            return toRet;
        }

        private GestIT.BinaryTmpExp ApplyAssociative(int i, GestIT.TmpOperator op)
        {
            // the code assumes that the minimum value of i is -1
            if (i < -1)
            {
                return null;
            }
            BinaryTmpExp exp = new BinaryTmpExp();
            exp.Operator = op;
           
            int childIndex = Children.Count - 1 - i;
            if (i == Children.Count - 1 || Children.Count == 2)
            {
                // attach the event handlers only to the root of the
                // binary expression
                exp.Iterative = this.Iterative;
                AttachEvents(exp);
            }
            switch (i)
            {
                case -1:
                    // no elements in the Children list
                    return null;

                case 0:
                    // only one child, put it on the left operand
                    exp.LeftOperand =  Children[childIndex] != null ? Children[childIndex].InstantiateExp() : null;
                    return exp;

                case 1:
                    // we are at the end of the recursion, create directly a binary expression
                    exp.LeftOperand = Children[childIndex] != null ? Children[childIndex].InstantiateExp() : null;
                    exp.RightOperand = Children[childIndex + 1] != null ? Children[childIndex + 1].InstantiateExp() : null;
                    return exp;


                // create the associative expression applying the right associative property:
                // exp op exp op exp = (exp op (exp op exp))
                default:
                    exp.LeftOperand = Children[childIndex] != null ? Children[childIndex].InstantiateExp() : null;
                    exp.RightOperand = ApplyAssociative(i - 1, op);
                    return exp;

            }
        }


    }
}
